package com.xplay.xpocker.room.message;

import com.xplay.xpocker.constant.RoomTypeEnum;
import com.xplay.xpocker.entity.GameHiMahjongAction;
import com.xplay.xpocker.entity.GameRuMahjongLogs;
import com.xplay.xpocker.entity.GameRuRoom;
import com.xplay.xpocker.norm.AbstractStrategy;
import com.xplay.xpocker.observer.ChannelHandlerManager;
import com.xplay.xpocker.room.*;
import com.xplay.xpocker.room.mahjong.dto.MahjongMessage;
import com.xplay.xpocker.room.mahjong.dto.MahjongRoomInfo;
import com.xplay.xpocker.room.mahjong.dto.MahjongRoomRule;
import com.xplay.xpocker.room.mahjong.dto.MahjongStatusEnum;
import com.xplay.xpocker.service.GameHiMahjongActionService;
import com.xplay.xpocker.util.Assertion;

import com.xplay.xpocker.util.JacksonStringUtil;
import com.xplay.xpocker.dvo.AbstractRoom;
import com.xplay.xpocker.util.ObjectUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.TaskExecutor;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @param <T> 房间信息
 * @param <R> 消息信息
 */
@Slf4j
public abstract class AbstractActionStrategy<T, R> extends AbstractStrategy<T, R> {
    protected abstract int strategyName();

    @Autowired
    private TaskExecutor taskExecutor;

    @Autowired
    private GameHiMahjongActionService hiMahjongActionService;

    @PostConstruct
    public void registerFactory() {
        ROOM_TYPE_ACTION_STRATEGY.put(strategyName(), this);
    }

    public static final ConcurrentHashMap<Integer, AbstractActionStrategy<?, ?>> ROOM_TYPE_ACTION_STRATEGY = new ConcurrentHashMap<>();

    @Transactional(rollbackFor = Exception.class)
    public abstract void doMessage(T roomInfo, R messageInfo);


    public void message(GameRuRoom gameRuRoom, Map<String, Object> message) {
        Date startDate = new Date();
        RoomStrategy<MahjongRoomRule, MahjongRoomInfo, GameRuMahjongLogs> strategy = RoomStrategy.getRoomStrategy(RoomTypeEnum.of(gameRuRoom.getType()).getParent());
        Assertion.isNull(strategy, "房间策略未找到");
        AbstractRoom<MahjongRoomRule, MahjongRoomInfo, GameRuMahjongLogs> roomVO = strategy.loadRoom(gameRuRoom.getCode());
        RoomInfoTL.set(new RoomThreadContent<>(roomVO));
        try {
            R messageCase = JacksonStringUtil.objectCase(JacksonStringUtil.objectToString(message, false), getLast());
            doMessage((T) roomVO, messageCase);
            Date endDate = new Date();
            log.debug("\n=====房间:{},执行耗时:{}/ms=====\n执行内容:{}\n========================", gameRuRoom.getCode(), endDate.getTime() - startDate.getTime(), message);
            /**
             * 转发消息前 将房间的版本号写入进去
             * @TODO
             * ChannelHandlerManager.dissolution(room.getCode ());
             * 这个版本号有很大的作用，用于客户端网路异常的时候，没有收到消息，进行房间信息同步
             *
             */
            List<Message> messageList = RoomInfoTL.getMessageToClient();
            if (ObjectUtils.notEmpty(messageList)) {
                List<MessageToClient> toClientMessage = messageList.stream().map(item -> item.getMessage()).filter((item) -> {
                    if (item.getBody() instanceof MessageToClient) {
                        return true;
                    }
                    return false;
                }).map(item -> (MessageToClient) item.getBody()).collect(Collectors.toList());
                strategy.saveActionLog(toClientMessage, roomVO);
                // 存储
                log.debug("拿到通知客户端的消息:{}", toClientMessage.size());
                for (Message messageToClient : messageList) {
                    taskExecutor.execute(() -> {
                        MessageContent<?> content = messageToClient.message;
                        content.setSyncVersion(roomVO.getGameInfo().getVersion());
                        // log.debug(messageToClient.toString());
                        if (messageToClient.delay > 0) {
                            try {
                                Thread.sleep((long) (messageToClient.delay * 1000));
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }
                        }
                        ChannelHandlerManager.getInstance().notifyMessage(content);
                    });
                }
            }
            if (roomVO.getStatus().equals(MahjongStatusEnum.OVER.getStatus())) {
                taskExecutor.execute(() -> {
                    try {
                        Thread.sleep(2000);
                        RoomStrategy.getRoomStrategy(roomVO.getCode()).cleanRoom(roomVO);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                });
            }
        } finally {
            RoomInfoTL.clear();
        }
    }
}
